#include "EditorWindowView.h"
#include "EditorWindowViewModel.h"

#include "Widgets/Window.h"
#include "Widgets/RootWidget.h"
#include "Widgets/ImageWidget.h"
#include "Widgets/Positioners/HorizontalPositionerWidget.h"
#include "Widgets/RectangleWidget.h"
#include "Widgets/Mouse/MouseWidget.h"

#include "PageArea/EditorPageAreaManagerView.h"
#include "Object/NewObject.h"

#include "Framework/Editor.h"
#include "Theme/EditorTheme.h"

void REditorWindowView::BindViewModel(TSharedObjectPtr<REditorWindowViewModel> InViewModel)
{
    BaseViewModel= InViewModel;

    PageAreaView=NewObject<REditorPageAreaManagerView>(this);
    PageAreaView->BindViewModel(BaseViewModel->GetPageArea());
}

TSharedObjectPtr<REditorWindowViewModel> REditorWindowView::GetViewModel()
{
    return BaseViewModel;
}

void REditorWindowView::UnbindViewModel()
{
    if(Window)
    {
        Window->Close();
    }
}

void REditorWindowView::CreateAndShowWindow()
{
    assert(!Window);
    Window= NewObject<RWindow>(this);
    Window->HideSystemBorder();
    Window->Show();
    RWindowWeakPtr WindowWeakPtr=Window;
    Window->OnGotFocus.BindLambda([this](){
        BaseViewModel->OnGotUserFocus();
    });

    Window->GetRootWidget()->FillColor=REditor::Get()->CurrentTheme->BackgroundColor;

    Window->SetWindowTitle(BaseViewModel->GetWindowTitle());
    BaseViewModel->OnWindowTitleChanged.Bind([WindowWeakPtr,this](){
        WindowWeakPtr->SetWindowTitle(BaseViewModel->GetWindowTitle());
    });

    auto UpdateWindowSize([WindowWeakPtr,this](){
        if(BaseViewModel->GetWindowSizeType()!=EWindowSizeType::Normal)
        {
            return;
        }
        WindowWeakPtr->SetWindowSize(BaseViewModel->GetWindowSize());
    });
    //UpdateWindowSize();
    BaseViewModel->OnWindowSizeChanged.Bind(UpdateWindowSize);

    auto UpdateWindowPosition([WindowWeakPtr,this](){
        if(BaseViewModel->GetWindowSizeType()!=EWindowSizeType::Normal)
        {
            return;
        }
        auto NewPosition=BaseViewModel->GetWindowPosition(WindowWeakPtr);
        WindowWeakPtr->SetWindowPosition(BaseViewModel->GetWindowPosition(WindowWeakPtr));
    });
    //UpdateWindowPosition();
    BaseViewModel->OnWindowPositionChanged.Bind(UpdateWindowPosition);

    auto UpdateWindowSizeType=[
        WindowWeakPtr,this,UpdateWindowSize,UpdateWindowPosition](){
        switch(BaseViewModel->GetWindowSizeType())
        {
            case EWindowSizeType::Normal:
                WindowWeakPtr->Restore();
                UpdateWindowSize();
                UpdateWindowPosition();
                break;
            case EWindowSizeType::Maximized:
                WindowWeakPtr->Maximize();
                break;
            case EWindowSizeType::Minimized:
                WindowWeakPtr->Minimize();
                break;
            case EWindowSizeType::FullScreen:
                WindowWeakPtr->FullScreen();
                break;
        }
    };
    UpdateWindowSizeType();
    BaseViewModel->OnWindowSizeTypeChanged.Bind(UpdateWindowSizeType);

    //enable move window by drag title bar
    //and maximize/resotre window by double click title bar
    auto DragTitleMouse=NewWidget<RMouseWidget>(WindowWeakPtr->GetRootWidget());
    {
        DragTitleMouse->Height=BaseViewModel->TitleBarHeight;
        DragTitleMouse->Anchor.HorizontalFill(WindowWeakPtr->GetRootWidget());
        DragTitleMouse->Drag->bIsEnabled=true;

        DragTitleMouse->Drag->OnDragStarted.Bind([WindowWeakPtr,this](){
            if(this->BaseViewModel->GetWindowSizeType()==EWindowSizeType::Maximized)
            {
                //make sure window is under mouse when dragging
                int WindowWidth=this->BaseViewModel->GetWindowSize().X;
                SIntPoint NewPosition=SMouseButtonEvent::GetMosuePositionOnScreen()
                        -SVec2(WindowWidth/2,this->BaseViewModel->TitleBarHeight/2);
                this->BaseViewModel->SetWindowPosition(NewPosition);
            }

            WindowWeakPtr->StartCaptureMouse();

            this->BaseViewModel->MousePositionOnDragStarted=SMouseButtonEvent::GetMosuePositionOnScreen();
            this->BaseViewModel->WindowPositionOnDragStarted=this->BaseViewModel->GetWindowPosition(WindowWeakPtr);
            this->BaseViewModel->bIsDraggingWindow=true;
            this->BaseViewModel->SetWindowSizeType(EWindowSizeType::Normal);
        

        });

        DragTitleMouse->Drag->OnDragMoved.Bind(
            [this,WindowWeakPtr](const SVec2& TotalDelta){
                auto MousePosition=SMouseButtonEvent::GetMosuePositionOnScreen();
                auto Delta=MousePosition-this->BaseViewModel->MousePositionOnDragStarted;
                this->BaseViewModel->SetWindowPosition(this->BaseViewModel->WindowPositionOnDragStarted+Delta);
        });


        DragTitleMouse->Drag->OnDragEnded.Bind(
            [this,WindowWeakPtr](){
            WindowWeakPtr->StopCaptureMouse();
            this->BaseViewModel->bIsDraggingWindow=false;
        });

        DragTitleMouse->OnDoubleClicked.BindLambda([this](const SMouseButtonEvent& InMouseEvent){
            if(this->BaseViewModel->GetWindowSizeType()==EWindowSizeType::Normal)
            {
                this->BaseViewModel->SetWindowSizeType(EWindowSizeType::Maximized);
            }
            else
            {
                this->BaseViewModel->SetWindowSizeType(EWindowSizeType::Normal);
            }
            return true;
        });
    }


    //add resize border
    {
        //use 8 mouse widget to detect resize border
        //and change mouse cursor
        auto OnDragStarted=[WindowWeakPtr,this](){
            WindowWeakPtr->StartCaptureMouse();
            this->BaseViewModel->WindowSizeOnResizeStarted=this->BaseViewModel->GetWindowSize();
            this->BaseViewModel->WindowPositionOnResizeStarted=this->BaseViewModel->GetWindowPosition(WindowWeakPtr);
            this->BaseViewModel->MousePositionOnResizeStarted=SMouseButtonEvent::GetMosuePositionOnScreen();
            this->BaseViewModel->bIsResizingWindow=true;
        };

        auto OnDragEnded=[WindowWeakPtr,this](){
            WindowWeakPtr->StopCaptureMouse();
            this->BaseViewModel->bIsResizingWindow=false;
        };


        auto TopBorder=NewWidget<RMouseWidget>(WindowWeakPtr->GetRootWidget());
        TopBorder->Height=BaseViewModel->ResizeBorderSize;
        TopBorder->Anchor.HorizontalFill(WindowWeakPtr->GetRootWidget());
        TopBorder->CursorShape=EMouseCursorType::ResizeVertical;
        TopBorder->Drag->bIsEnabled=true;
        TopBorder->Visible=[this](){
            return this->BaseViewModel->IsResizable();
        };

        TopBorder->Drag->OnDragStarted.Bind(OnDragStarted);
        TopBorder->Drag->OnDragEnded.Bind(OnDragEnded);
        TopBorder->Drag->OnDragMoved.Bind([this,WindowWeakPtr](const SVec2&){
            auto MousePosition=SMouseButtonEvent::GetMosuePositionOnScreen();
            auto Delta=MousePosition-this->BaseViewModel->MousePositionOnResizeStarted;
            auto NewSize=this->BaseViewModel->WindowSizeOnResizeStarted;
            NewSize.Y-=Delta.Y;
            this->BaseViewModel->SetWindowSize(NewSize);
            auto NewPosition=this->BaseViewModel->WindowPositionOnResizeStarted;
            NewPosition.Y+=Delta.Y;
            this->BaseViewModel->SetWindowPosition(NewPosition);
        });


        auto BottomBorder=NewWidget<RMouseWidget>(WindowWeakPtr->GetRootWidget());
        BottomBorder->Height=BaseViewModel->ResizeBorderSize;
        BottomBorder->Anchor.HorizontalFill(WindowWeakPtr->GetRootWidget());
        BottomBorder->Anchor.BottomReference=WindowWeakPtr->GetRootWidget();
        BottomBorder->CursorShape=EMouseCursorType::ResizeVertical;
        BottomBorder->Drag->bIsEnabled=true;
        BottomBorder->Visible=[this](){
            return this->BaseViewModel->IsResizable();
        };

        BottomBorder->Drag->OnDragStarted.Bind(OnDragStarted);
        BottomBorder->Drag->OnDragEnded.Bind(OnDragEnded);
        BottomBorder->Drag->OnDragMoved.Bind([this,WindowWeakPtr](const SVec2&){
            auto MousePosition=SMouseButtonEvent::GetMosuePositionOnScreen();
            auto Delta=MousePosition-this->BaseViewModel->MousePositionOnResizeStarted;
            auto NewSize=this->BaseViewModel->WindowSizeOnResizeStarted;
            NewSize.Y+=Delta.Y;
            this->BaseViewModel->SetWindowSize(NewSize);
        });

        auto LeftBorder=NewWidget<RMouseWidget>(WindowWeakPtr->GetRootWidget());
        LeftBorder->Width=BaseViewModel->ResizeBorderSize;
        LeftBorder->Anchor.VerticalFill(WindowWeakPtr->GetRootWidget());
        LeftBorder->CursorShape=EMouseCursorType::ResizeHorizontal;
        LeftBorder->Drag->bIsEnabled=true;
        LeftBorder->Visible=[this](){
            return this->BaseViewModel->IsResizable();
        };

        LeftBorder->Drag->OnDragStarted.Bind(OnDragStarted);
        LeftBorder->Drag->OnDragEnded.Bind(OnDragEnded);
        LeftBorder->Drag->OnDragMoved.Bind([this,WindowWeakPtr](const SVec2&){
            auto MousePosition=SMouseButtonEvent::GetMosuePositionOnScreen();
            auto Delta=MousePosition-this->BaseViewModel->MousePositionOnResizeStarted;
            auto NewSize=this->BaseViewModel->WindowSizeOnResizeStarted;
            NewSize.X-=Delta.X;
            this->BaseViewModel->SetWindowSize(NewSize);
            auto NewPosition=this->BaseViewModel->WindowPositionOnResizeStarted;
            NewPosition.X+=Delta.X;
            this->BaseViewModel->SetWindowPosition(NewPosition);
        });

        auto RightBorder=NewWidget<RMouseWidget>(WindowWeakPtr->GetRootWidget());
        RightBorder->Width=BaseViewModel->ResizeBorderSize;
        RightBorder->Anchor.VerticalFill(WindowWeakPtr->GetRootWidget());
        RightBorder->Anchor.RightReference=WindowWeakPtr->GetRootWidget();
        RightBorder->CursorShape=EMouseCursorType::ResizeHorizontal;
        RightBorder->Drag->bIsEnabled=true;
        RightBorder->Visible=[this](){
            return this->BaseViewModel->IsResizable();
        };

        RightBorder->Drag->OnDragStarted.Bind(OnDragStarted);
        RightBorder->Drag->OnDragEnded.Bind(OnDragEnded);
        RightBorder->Drag->OnDragMoved.Bind([this,WindowWeakPtr](const SVec2&){
            auto MousePosition=SMouseButtonEvent::GetMosuePositionOnScreen();
            auto Delta=MousePosition-this->BaseViewModel->MousePositionOnResizeStarted;
            auto NewSize=this->BaseViewModel->WindowSizeOnResizeStarted;
            NewSize.X+=Delta.X;
            this->BaseViewModel->SetWindowSize(NewSize);
        });

        auto TopLeftCorner=NewWidget<RMouseWidget>(WindowWeakPtr->GetRootWidget());
        TopLeftCorner->Width=BaseViewModel->ResizeBorderSize;
        TopLeftCorner->Height=BaseViewModel->ResizeBorderSize;
        TopLeftCorner->CursorShape=EMouseCursorType::ResizeLeftTopTORightBottom;
        TopLeftCorner->Drag->bIsEnabled=true;
        TopLeftCorner->Visible=[this](){
            return this->BaseViewModel->IsResizable();
        };

        TopLeftCorner->Drag->OnDragStarted.Bind(OnDragStarted);
        TopLeftCorner->Drag->OnDragEnded.Bind(OnDragEnded);
        TopLeftCorner->Drag->OnDragMoved.Bind([this,WindowWeakPtr](const SVec2&){
            auto MousePosition=SMouseButtonEvent::GetMosuePositionOnScreen();
            auto Delta=MousePosition-this->BaseViewModel->MousePositionOnResizeStarted;
            auto NewSize=this->BaseViewModel->WindowSizeOnResizeStarted;
            NewSize.X-=Delta.X;
            NewSize.Y-=Delta.Y;
            this->BaseViewModel->SetWindowSize(NewSize);
            auto NewPosition=this->BaseViewModel->WindowPositionOnResizeStarted;
            NewPosition.X+=Delta.X;
            NewPosition.Y+=Delta.Y;
            this->BaseViewModel->SetWindowPosition(NewPosition);
        });

        auto TopRightCorner=NewWidget<RMouseWidget>(WindowWeakPtr->GetRootWidget());
        TopRightCorner->Width=BaseViewModel->ResizeBorderSize;
        TopRightCorner->Height=BaseViewModel->ResizeBorderSize;
        TopRightCorner->Anchor.RightReference=WindowWeakPtr->GetRootWidget();
        TopRightCorner->CursorShape=EMouseCursorType::ResizeRightTopTOLeftBottom;
        TopRightCorner->Drag->bIsEnabled=true;
        TopRightCorner->Visible=[this](){
            return this->BaseViewModel->IsResizable();
        };

        TopRightCorner->Drag->OnDragStarted.Bind(OnDragStarted);
        TopRightCorner->Drag->OnDragEnded.Bind(OnDragEnded);
        TopRightCorner->Drag->OnDragMoved.Bind([this,WindowWeakPtr](const SVec2&){
            auto MousePosition=SMouseButtonEvent::GetMosuePositionOnScreen();
            auto Delta=MousePosition-this->BaseViewModel->MousePositionOnResizeStarted;
            auto NewSize=this->BaseViewModel->WindowSizeOnResizeStarted;
            NewSize.X+=Delta.X;
            NewSize.Y-=Delta.Y;
            this->BaseViewModel->SetWindowSize(NewSize);
            auto NewPosition=this->BaseViewModel->WindowPositionOnResizeStarted;
            NewPosition.Y+=Delta.Y;
            this->BaseViewModel->SetWindowPosition(NewPosition);
        });

        auto BottomLeftCorner=NewWidget<RMouseWidget>(WindowWeakPtr->GetRootWidget());
        BottomLeftCorner->Width=BaseViewModel->ResizeBorderSize;
        BottomLeftCorner->Height=BaseViewModel->ResizeBorderSize;
        BottomLeftCorner->Anchor.BottomReference=WindowWeakPtr->GetRootWidget();
        BottomLeftCorner->CursorShape=EMouseCursorType::ResizeRightTopTOLeftBottom;
        BottomLeftCorner->Drag->bIsEnabled=true;
        BottomLeftCorner->Visible=[this](){
            return this->BaseViewModel->IsResizable();
        };

        BottomLeftCorner->Drag->OnDragStarted.Bind(OnDragStarted);
        BottomLeftCorner->Drag->OnDragEnded.Bind(OnDragEnded);
        BottomLeftCorner->Drag->OnDragMoved.Bind([this,WindowWeakPtr](const SVec2&){
            auto MousePosition=SMouseButtonEvent::GetMosuePositionOnScreen();
            auto Delta=MousePosition-this->BaseViewModel->MousePositionOnResizeStarted;
            auto NewSize=this->BaseViewModel->WindowSizeOnResizeStarted;
            NewSize.X-=Delta.X;
            NewSize.Y+=Delta.Y;
            this->BaseViewModel->SetWindowSize(NewSize);
            auto NewPosition=this->BaseViewModel->WindowPositionOnResizeStarted;
            NewPosition.X+=Delta.X;
            this->BaseViewModel->SetWindowPosition(NewPosition);
        });

        auto BottomRightCorner=NewWidget<RMouseWidget>(WindowWeakPtr->GetRootWidget());
        BottomRightCorner->Width=BaseViewModel->ResizeBorderSize;
        BottomRightCorner->Height=BaseViewModel->ResizeBorderSize;
        BottomRightCorner->Anchor.RightReference=WindowWeakPtr->GetRootWidget();
        BottomRightCorner->Anchor.BottomReference=WindowWeakPtr->GetRootWidget();
        BottomRightCorner->CursorShape=EMouseCursorType::ResizeLeftTopTORightBottom;
        BottomRightCorner->Drag->bIsEnabled=true;
        BottomRightCorner->Visible=[this](){
            return this->BaseViewModel->IsResizable();
        };

        BottomRightCorner->Drag->OnDragStarted.Bind(OnDragStarted);
        BottomRightCorner->Drag->OnDragEnded.Bind(OnDragEnded);
        BottomRightCorner->Drag->OnDragMoved.Bind([this,WindowWeakPtr](const SVec2&){
            auto MousePosition=SMouseButtonEvent::GetMosuePositionOnScreen();
            auto Delta=MousePosition-this->BaseViewModel->MousePositionOnResizeStarted;
            auto NewSize=this->BaseViewModel->WindowSizeOnResizeStarted;
            NewSize.X+=Delta.X;
            NewSize.Y+=Delta.Y;
            this->BaseViewModel->SetWindowSize(NewSize);
        });

    }


    //add window buttons
    auto WindowButtons= NewWidget<RHorizontalPositionerWidget>(WindowWeakPtr->GetRootWidget());
    WindowButtons->Anchor.RightReference=WindowWeakPtr->GetRootWidget();
    WindowButtons->Width=BaseViewModel->WindowButtonWidth*3;
    WindowButtons->Height=BaseViewModel->TitleBarHeight;
    WindowButtons->Spacing=0;
    WindowButtonsWidget=WindowButtons;
    //Minimize
    {
        auto Minimize= NewWidget<RRectangleWidget>(WindowButtons);
        Minimize->FillColor=BaseViewModel->WindowsButtonBackgroundColor;
        Minimize->Width=BaseViewModel->WindowButtonWidth;
        Minimize->Anchor.VerticalFill(WindowButtons);

        auto MinimizeImage=NewWidget<RImageWidget>(Minimize);
        MinimizeImage->Image=BaseViewModel->GetMinimizeButtonImage();
        MinimizeImage->Width=BaseViewModel->WindowButtonImageSize;
        MinimizeImage->Height=BaseViewModel->WindowButtonImageSize;
        MinimizeImage->Anchor.CenterIn(Minimize);

        auto Mouse=NewWidget<RMouseWidget>(Minimize);
        Mouse->Anchor.Fill(Minimize);
        TWeakObjectPtr<RMouseWidget> MouseWeakPtr=Mouse;
            Minimize->Fill=[MouseWeakPtr]()->bool{
                return MouseWeakPtr->IsHovered();
        };

        Mouse->OnClicked.BindLambda([this](const SMouseButtonEvent&){
            this->BaseViewModel->SetWindowSizeType(EWindowSizeType::Minimized);
            return true;
        });
    }

    //maximize button
    {
        auto Maximize= NewWidget<RRectangleWidget>(WindowButtons);
        Maximize->FillColor=BaseViewModel->WindowsButtonBackgroundColor;
        Maximize->Width=BaseViewModel->WindowButtonWidth;
        Maximize->Anchor.VerticalFill(WindowButtons);

        auto MaximizeImage=NewWidget<RImageWidget>(Maximize);
        MaximizeImage->Image=[this]()
        {
            return BaseViewModel->GetMaximizeButtonImage();
        };
        MaximizeImage->Width=BaseViewModel->WindowButtonImageSize;
        MaximizeImage->Height=BaseViewModel->WindowButtonImageSize;
        MaximizeImage->Anchor.CenterIn(Maximize);

        auto Mouse=NewWidget<RMouseWidget>(Maximize);
        Mouse->Anchor.Fill(Maximize);
        TWeakObjectPtr<RMouseWidget> MouseWeakPtr=Mouse;
            Maximize->Fill=[MouseWeakPtr]()->bool{
                return MouseWeakPtr->IsHovered();
        };

        Mouse->OnClicked.BindLambda([this](const SMouseButtonEvent&){
            if(this->BaseViewModel->GetWindowSizeType()==EWindowSizeType::Normal)
            {
                this->BaseViewModel->SetWindowSizeType(EWindowSizeType::Maximized);
            }
            else
            {
                this->BaseViewModel->SetWindowSizeType(EWindowSizeType::Normal);
            }
            return true;
        });
    }

    //close button
    {
        auto Close= NewWidget<RRectangleWidget>(WindowButtons);
        Close->FillColor=BaseViewModel->WindowsCloseBackgroundColor;
        Close->Width=BaseViewModel->WindowButtonWidth;
        Close->Anchor.VerticalFill(WindowButtons);

        auto CloseImage=NewWidget<RImageWidget>(Close);
        CloseImage->Image=BaseViewModel->GetCloseButtonImage();
        CloseImage->Width=BaseViewModel->WindowButtonImageSize;
        CloseImage->Height=BaseViewModel->WindowButtonImageSize;
        CloseImage->Anchor.CenterIn(Close);

        auto Mouse=NewWidget<RMouseWidget>(Close);
        Mouse->Anchor.Fill(Close);
        TWeakObjectPtr<RMouseWidget> MouseWeakPtr=Mouse;
            Close->Fill=[MouseWeakPtr]()->bool{
                return MouseWeakPtr->IsHovered();
        };

        Mouse->OnClicked.BindLambda([this](const SMouseButtonEvent&){
            NotifyCloseButtonClicked();
            return true;
        });
    }
    
    auto LeftTopIcon=NewWidget<RImageWidget>(WindowWeakPtr->GetRootWidget());
    LeftTopIcon->Image=BaseViewModel->GetWindowLeftTopIcon();
    LeftTopIcon->Width=BaseViewModel->WindowLeftTopIconSize;
    LeftTopIcon->Height=BaseViewModel->WindowLeftTopIconSize;
    LeftTopIcon->X=BaseViewModel->WindowLeftTopIconLeftMargin;
    LeftTopIcon->Y=BaseViewModel->TitleBarHeight/2-BaseViewModel->WindowLeftTopIconSize/2;
    
    auto BodyBackground=NewWidget<RRectangleWidget>(WindowWeakPtr->GetRootWidget());
    BodyBackground->Anchor.HorizontalFill(WindowWeakPtr->GetRootWidget());
    BodyBackground->Anchor.TopReference=WindowWeakPtr->GetRootWidget();
    BodyBackground->Anchor.TopOffset=BaseViewModel->TitleBarHeight;
    BodyBackground->Anchor.BottomReference= WindowWeakPtr->GetRootWidget();
    BodyBackground->Anchor.BottomOffset= BaseViewModel->StatusBarHeight;
    BodyBackground->FillColor=BaseViewModel->SpliterColor;

    WindowContentWidget=NewWidget<RWidget>(BodyBackground);
    WindowContentWidget->Anchor.TopReference= BodyBackground;
    WindowContentWidget->Anchor.TopOffset= BaseViewModel->SpliterSize;
    WindowContentWidget->Anchor.BottomReference= BodyBackground;
    WindowContentWidget->Anchor.BottomOffset= BaseViewModel->SpliterSize;
    WindowContentWidget->Anchor.HorizontalFill(BodyBackground);
    

}

TSharedObjectPtr<REditorPageAreaManagerView> REditorWindowView::GetPageArea()
{
    return PageAreaView;
}
